home *** CD-ROM | disk | FTP | other *** search
- // Abacus.c
- //
- // A simple decimal and hexadecimal abacus
- // Copyright © 1995 Gary Kacmarcik
- // All rights reserved.
- //
- // Modification History:
- // 22 JUN 95 GJK began at MacHack
- // 24 JUN 95 GJK finished 1st version
-
-
- // Magic Cap stuff
- #include "Magic.h"
- #include "Debug.h"
- #include "Package.h"
-
-
- // required by linker
- main() {}
-
- #undef CURRENTCLASS
- #pragma segment Abacus
- #define CURRENTCLASS Abacus
-
- #define iHexOnly MakePackageIndexical(27,1)
-
- #define iiAbacusBead MakePackageFlatIndexical(25)
- #define iiAbacus MakePackageFlatIndexical(26)
-
- #define NUM_ROWS 16
-
- // height of content area of bar
- #define BAR_HEIGHT 0x0C00
- #define BAR_TOTAL (BAR_HEIGHT + 0x0400)
- #define BAR_TOP_OFFSET (BAR_TOTAL/2)
-
- #define BEAD_WIDTH 0x1000
- #define BEAD_WIDTH_2 0x0800
-
- #define ROW_SPACING 0x0400
-
- Method void
- Abacus_SetEndian(ObjectID self, SignedShort value)
- {
- SetField(self, endian, value);
- DirtyContent(iiAbacus);
- RedrawNow();
- }
-
- Method void
- Abacus_SetBase(ObjectID self, SignedShort value)
- {
- SetField(self, base, 0);
- PlaySound(iMagicSound);
- }
-
- Method void
- Abacus_Draw(ObjectID self, ObjectID canvas, ObjectID clip)
- {
- Box bBox, bLine;
- Dot dOrigin;
- Dot where;
- Abacus_Fields fields;
- int i,j;
- int nOn, nOff;
- int nRowValue;
- ObjectID idText;
- TextRange range;
-
- InheritedDraw(self, canvas, clip);
-
- // get a ptr to our fields so that we can access them more directly
- ReadFields(self, &fields);
-
- ContentBox(self, &bBox);
- EraseBox(canvas, clip, &bBox);
-
- // draw the wires
- bLine.top = bBox.top;
- bLine.bottom = bBox.bottom;
- bLine.left = bBox.left + BEAD_WIDTH_2 + ROW_SPACING - 0x0100;
- for (i=0; i<NUM_ROWS; i++)
- {
- bLine.right = bLine.left + 0x0300;
- FillBox(canvas, clip, &bLine, rgbBlack, pixelCopy);
- bLine.left += BEAD_WIDTH + ROW_SPACING;
- }
-
- // draw the horizontal middle bar
- bLine.top = (bBox.top + bBox.bottom)/2 - BAR_TOP_OFFSET;
- bLine.bottom = bLine.top + 0x0100;
- bLine.left = bBox.left;
- bLine.right = bBox.right;
- FillBox(canvas, clip, &bLine, rgbBlack, pixelCopy);
- bLine.top += 0x0100;
- bLine.bottom = bLine.top + 0x0100;
- FillBox(canvas, clip, &bLine, rgbWhite, pixelCopy);
- bLine.top += 0x0100;
- bLine.bottom = bLine.top + BAR_HEIGHT;
- FillBox(canvas, clip, &bLine, rgbLtGray, pixelCopy);
- bLine.top += BAR_HEIGHT;
- bLine.bottom = bLine.top + 0x0100;
- FillBox(canvas, clip, &bLine, rgbDkGray, pixelCopy);
- bLine.top += 0x0100;
- bLine.bottom = bLine.top + 0x0100;
- FillBox(canvas, clip, &bLine, rgbBlack, pixelCopy);
-
- // draw the hex digits in the bar
- idText = NewTransient(Text_, nil);
- ReplaceTextWithString(idText, "\p0123456789ABCDEF");
- where.h = bBox.left + BEAD_WIDTH_2 + ROW_SPACING - 0x0200;
- where.v = (bBox.top + bBox.bottom + Ascent(iFatCaps10))/2 - 0x0100;
- range.length = 1;
- for (i=0; i<NUM_ROWS; i++)
- {
- range.start = (&fields.row0)[fields.endian ? i : NUM_ROWS-1-i];
- TextDraw(idText, &range, iFatCaps10, canvas, clip, &where);
- where.h += BEAD_WIDTH + ROW_SPACING;
- }
- Destroy(idText);
-
- // draw the beads
- dOrigin.h = bBox.left + BEAD_WIDTH_2 + ROW_SPACING;
- for (i=0; i<NUM_ROWS; i++)
- {
- nRowValue = (&fields.row0)[fields.endian ? i : NUM_ROWS-1-i];
-
- // each "on" top bead is worth 4
- nOn = (nRowValue & 0x000C) >> 2;
- nOff = 4 - nOn;
-
- // draw "off" top beads
- // start from top and work down
- dOrigin.v = bBox.top + BEAD_WIDTH_2;
- for (j=0; j<nOff; j++)
- {
- DrawImage(iiAbacusBead, canvas, clip, &dOrigin);
- dOrigin.v += BEAD_WIDTH;
- }
-
- // draw "on" top beads
- // start from top of middle bar and work up
- dOrigin.v = (bBox.top + bBox.bottom)/2 - BAR_TOP_OFFSET - BEAD_WIDTH_2;
- for (j=0; j<nOn; j++)
- {
- DrawImage(iiAbacusBead, canvas, clip, &dOrigin);
- dOrigin.v -= BEAD_WIDTH;
- }
-
- // each "on" bottom bead is worth 1
- nOn = nRowValue & 0x0003;
- nOff = 4 - nOn;
-
- // draw "off" bottom beads
- // start from bottom and work up
- dOrigin.v = bBox.bottom - BEAD_WIDTH_2;
- for (j=0; j<nOff; j++)
- {
- DrawImage(iiAbacusBead, canvas, clip, &dOrigin);
- dOrigin.v -= BEAD_WIDTH;
- }
-
- // draw "on" bottom beads
- // start from bottom of middle bar and work down
- dOrigin.v = (bBox.top + bBox.bottom)/2 - BAR_TOP_OFFSET + BAR_TOTAL + BEAD_WIDTH_2;
- for (j=0; j<nOn; j++)
- {
- DrawImage(iiAbacusBead, canvas, clip, &dOrigin);
- dOrigin.v += BEAD_WIDTH;
- }
-
- // next row
- dOrigin.h += BEAD_WIDTH + ROW_SPACING;
- }
- }
-
-
- Method void
- Abacus_Touch(ObjectID self, ObjectID touchInput)
- {
- Dot where;
- Box bBox;
- int i;
- int nTappedRow;
- int nRowLeft, nRowRight;
- int nBarTop;
- int nValue;
- int nBeadNum;
- Abacus_Fields *fields;
-
- // find where the tap was
- LatestPoint(touchInput, &where);
-
- ContentBox(self, &bBox);
- fields = BeginModifyFields(self);
-
- // first, find which row (if any) the tap was on
- nTappedRow = -1;
- nRowLeft = bBox.left + ROW_SPACING;
- nRowRight = nRowLeft + BEAD_WIDTH;
- for (i=0; i<NUM_ROWS; i++)
- {
- if (where.h > nRowLeft && where.h < nRowRight)
- nTappedRow = i;
- nRowLeft += BEAD_WIDTH + ROW_SPACING;
- nRowRight = nRowLeft + BEAD_WIDTH;
- }
- // bail if the tap wasn't within a row
- if (nTappedRow == -1)
- return;
- if (!fields->endian)
- nTappedRow = NUM_ROWS-1 - nTappedRow;
-
- // was the click in the horizontal bar?
- nBarTop = (bBox.top + bBox.bottom)/2 - BAR_TOP_OFFSET;
- if (where.v > nBarTop && where.v < (nBarTop + BAR_TOTAL))
- // we don't do anything here
- return;
-
- nValue = (&fields->row0)[nTappedRow];
- if ((nBeadNum = FindBead(where, nValue, &bBox)) != -1)
- {
- int nNewValue;
- int nOnBeads;
- ObjectID idSound;
-
- // check upper beads
- if (nBeadNum < 4)
- {
- nOnBeads = (nValue & 0x000C) >> 2;
- if ((4-nOnBeads) > nBeadNum)
- { // down (adding 4-beads)
- idSound = iMagicSound;
- nNewValue = nValue + 4 * (4-nOnBeads-nBeadNum);
- }
- else
- { // up (subtracting 4-beads)
- idSound = iMagicSound;
- nNewValue = nValue - 4 * (nOnBeads+nBeadNum-3);
- }
- }
-
- // check lower beads
- else
- {
- nOnBeads = (nValue & 0x0003);
- nBeadNum -= 4;
- if (nOnBeads > nBeadNum)
- { // down (subtracting 1-beads)
- idSound = iMagicSound;
- nNewValue = nValue - (nOnBeads-nBeadNum);
- }
- else
- { // up (adding 1-beads)
- idSound = iMagicSound;
- nNewValue = nValue + (4-nOnBeads+nBeadNum-3);
- }
- }
-
- UpdateRow(fields, nTappedRow, nNewValue);
-
- // PlaySound(idSound);
- DirtyContent(self);
- RedrawNow();
- }
- EndModify(self);
- }
-
-
- // update the row with the given value
- // this assumes that it is called in a BeginModifyFields/EndModify struct
- // make sure that the carry beads get propagated
- Private void
- UpdateRow(Abacus_Fields *fields, int row, int newVal)
- {
- (&fields->row0)[row] = (newVal & 0x000F);
- for (row++; newVal > 0x000F && row < NUM_ROWS; row++)
- {
- newVal = (&fields->row0)[row] + 1;
- (&fields->row0)[row] = (newVal & 0x000F);
- }
- }
-
-
- // given a point and a row, this will return the bead that contains the point.
- Private int
- FindBead(Dot where, int nValue, Box *bBox)
- {
- int nTapOnTopPart;
- int nBeadTop;
- int nTop, nBottom;
- int nBase;
- int i,j;
-
- // was the click in the upper or lower portion of the abacus?
- if (where.v < (bBox->top + bBox->bottom)/2 - BAR_TOP_OFFSET)
- {
- nTapOnTopPart = 1;
- nTop = bBox->top;
- nBottom = (bBox->top + bBox->bottom)/2 - BAR_TOP_OFFSET;
- nValue = (nValue & 0x000C) >> 2;
- nBase = 0;
- }
- else
- {
- nTapOnTopPart = 0;
- nTop = (bBox->top + bBox->bottom)/2 - BAR_TOP_OFFSET + BAR_TOTAL;
- nBottom = bBox->bottom;
- // we reverse the "on" and "off" bits here so that the following
- // calculations can be shared with the upper part
- nValue = 4 - (nValue & 0x0003);
- nBase = 4;
- }
-
- // at this point, nValue contains the # of beads at the bottom part of
- // the tapped section of the abacus. for the upper part, this corresponds
- // to "on" beads, for the lower part, this corresponds to "off" beads.
-
- // check the upper beads
- nBeadTop = nTop;
- for (i=0; i<(4-nValue) ;i++, nBeadTop += BEAD_WIDTH)
- if (where.v > nBeadTop && where.v < nBeadTop + BEAD_WIDTH)
- return nBase + i;
-
- // check the lower beads
- nBeadTop = nBottom - BEAD_WIDTH;
- for (i=3, j=0; j<nValue; i--, j++, nBeadTop -= BEAD_WIDTH)
- if (where.v > nBeadTop && where.v < nBeadTop + BEAD_WIDTH)
- return nBase + i;
-
- return -1;
- }
-
- #undef CURRENTCLASS
-